In [177]:
print("Projekt wykonali:\n- Kamil Skałbania\n- Paweł Wypych\n- Miłosz Gronowski")
Projekt wykonali: - Kamil Skałbania - Paweł Wypych - Miłosz Gronowski
In [178]:
import pandas as pd
import numpy as np
data = pd.read_csv("song_data.csv")
print(data.columns)
Index(['song_name', 'song_popularity', 'song_duration_ms', 'acousticness',
'danceability', 'energy', 'instrumentalness', 'key', 'liveness',
'loudness', 'audio_mode', 'speechiness', 'tempo', 'time_signature',
'audio_valence'],
dtype='object')
In [179]:
data.head(50)
Out[179]:
| song_name | song_popularity | song_duration_ms | acousticness | danceability | energy | instrumentalness | key | liveness | loudness | audio_mode | speechiness | tempo | time_signature | audio_valence | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Boulevard of Broken Dreams | 73 | 262333 | 0.005520 | 0.496 | 0.682 | 0.000029 | 8 | 0.0589 | -4.095 | 1 | 0.0294 | 167.060 | 4 | 0.4740 |
| 1 | In The End | 66 | 216933 | 0.010300 | 0.542 | 0.853 | 0.000000 | 3 | 0.1080 | -6.407 | 0 | 0.0498 | 105.256 | 4 | 0.3700 |
| 2 | Seven Nation Army | 76 | 231733 | 0.008170 | 0.737 | 0.463 | 0.447000 | 0 | 0.2550 | -7.828 | 1 | 0.0792 | 123.881 | 4 | 0.3240 |
| 3 | By The Way | 74 | 216933 | 0.026400 | 0.451 | 0.970 | 0.003550 | 0 | 0.1020 | -4.938 | 1 | 0.1070 | 122.444 | 4 | 0.1980 |
| 4 | How You Remind Me | 56 | 223826 | 0.000954 | 0.447 | 0.766 | 0.000000 | 10 | 0.1130 | -5.065 | 1 | 0.0313 | 172.011 | 4 | 0.5740 |
| 5 | Bring Me To Life | 80 | 235893 | 0.008950 | 0.316 | 0.945 | 0.000002 | 4 | 0.3960 | -3.169 | 0 | 0.1240 | 189.931 | 4 | 0.3200 |
| 6 | Last Resort | 81 | 199893 | 0.000504 | 0.581 | 0.887 | 0.001110 | 4 | 0.2680 | -3.659 | 0 | 0.0624 | 90.578 | 4 | 0.7240 |
| 7 | Are You Gonna Be My Girl | 76 | 213800 | 0.001480 | 0.613 | 0.953 | 0.000582 | 2 | 0.1520 | -3.435 | 1 | 0.0855 | 105.046 | 4 | 0.5370 |
| 8 | Mr. Brightside | 80 | 222586 | 0.001080 | 0.330 | 0.936 | 0.000000 | 1 | 0.0926 | -3.660 | 1 | 0.0917 | 148.112 | 4 | 0.2340 |
| 9 | Sex on Fire | 81 | 203346 | 0.001720 | 0.542 | 0.905 | 0.010400 | 9 | 0.1360 | -5.653 | 1 | 0.0540 | 153.398 | 4 | 0.3740 |
| 10 | The Middle | 78 | 168253 | 0.042400 | 0.629 | 0.897 | 0.000000 | 2 | 0.2630 | -3.401 | 1 | 0.0483 | 161.944 | 4 | 0.9300 |
| 11 | Numb | 63 | 185586 | 0.004600 | 0.496 | 0.863 | 0.000000 | 9 | 0.6390 | -4.153 | 1 | 0.0381 | 110.018 | 4 | 0.2430 |
| 12 | Smooth Criminal | 75 | 209266 | 0.004340 | 0.647 | 0.964 | 0.003600 | 9 | 0.1500 | -4.225 | 0 | 0.0600 | 126.942 | 4 | 0.8750 |
| 13 | Can't Stop | 81 | 269000 | 0.017900 | 0.618 | 0.938 | 0.000000 | 9 | 0.1670 | -3.442 | 1 | 0.0456 | 91.455 | 4 | 0.8750 |
| 14 | Chop Suey! | 69 | 210240 | 0.000353 | 0.420 | 0.929 | 0.000747 | 7 | 0.1220 | -3.899 | 0 | 0.1210 | 127.204 | 4 | 0.3000 |
| 15 | Take Me Out | 77 | 237026 | 0.000423 | 0.278 | 0.676 | 0.000899 | 9 | 0.1360 | -8.821 | 1 | 0.0371 | 104.545 | 4 | 0.4940 |
| 16 | I Miss You | 71 | 227240 | 0.001360 | 0.659 | 0.778 | 0.000007 | 11 | 0.0841 | -6.423 | 1 | 0.0379 | 110.022 | 4 | 0.6230 |
| 17 | Best of You | 62 | 256600 | 0.007010 | 0.370 | 0.944 | 0.000003 | 1 | 0.1350 | -4.979 | 0 | 0.0767 | 130.315 | 4 | 0.3450 |
| 18 | I Write Sins Not Tragedies | 77 | 187613 | 0.093800 | 0.567 | 0.795 | 0.000000 | 9 | 0.1140 | -4.985 | 0 | 0.1340 | 170.060 | 4 | 0.6350 |
| 19 | Kryptonite | 79 | 233933 | 0.006640 | 0.545 | 0.865 | 0.000011 | 11 | 0.1680 | -5.708 | 0 | 0.0286 | 99.010 | 4 | 0.5430 |
| 20 | The Kill (Bury Me) | 69 | 231533 | 0.001400 | 0.309 | 0.912 | 0.000271 | 4 | 0.5820 | -3.881 | 0 | 0.0646 | 183.035 | 3 | 0.3020 |
| 21 | Use Somebody | 79 | 230760 | 0.005520 | 0.276 | 0.715 | 0.000417 | 0 | 0.2010 | -5.356 | 1 | 0.0432 | 137.028 | 4 | 0.1730 |
| 22 | No One Knows | 13 | 255066 | 0.013700 | 0.518 | 0.538 | 0.000398 | 0 | 0.1410 | -5.818 | 1 | 0.0486 | 170.953 | 4 | 0.6870 |
| 23 | Jerk It Out | 62 | 195666 | 0.017100 | 0.580 | 0.981 | 0.000011 | 8 | 0.3860 | -2.603 | 0 | 0.0600 | 134.007 | 4 | 0.8610 |
| 24 | Uprising | 77 | 304840 | 0.000202 | 0.602 | 0.905 | 0.064000 | 2 | 0.1170 | -4.046 | 1 | 0.0775 | 128.019 | 4 | 0.4110 |
| 25 | Hey There Delilah | 79 | 232533 | 0.872000 | 0.657 | 0.291 | 0.000000 | 2 | 0.1140 | -10.572 | 1 | 0.0293 | 103.973 | 4 | 0.2980 |
| 26 | Blurry | 28 | 303920 | 0.010200 | 0.440 | 0.932 | 0.000000 | 3 | 0.1420 | -4.488 | 0 | 0.0584 | 157.438 | 4 | 0.4950 |
| 27 | American Idiot | 78 | 176346 | 0.000026 | 0.380 | 0.988 | 0.000079 | 1 | 0.3680 | -2.042 | 1 | 0.0639 | 186.113 | 4 | 0.7690 |
| 28 | Welcome to the Black Parade | 77 | 311106 | 0.000289 | 0.217 | 0.905 | 0.000110 | 2 | 0.2220 | -4.103 | 1 | 0.0752 | 96.950 | 4 | 0.2360 |
| 29 | Gives You Hell | 71 | 213106 | 0.015600 | 0.714 | 0.725 | 0.000000 | 4 | 0.0726 | -6.411 | 1 | 0.0402 | 99.988 | 4 | 0.5900 |
| 30 | All My Life | 11 | 262733 | 0.000194 | 0.582 | 0.597 | 0.000273 | 5 | 0.5170 | -5.671 | 1 | 0.0512 | 167.738 | 4 | 0.6740 |
| 31 | Like a Stone | 77 | 293960 | 0.007970 | 0.614 | 0.568 | 0.000000 | 7 | 0.0997 | -5.477 | 0 | 0.0276 | 107.849 | 4 | 0.5160 |
| 32 | It's Been Awhile | 65 | 264706 | 0.001890 | 0.509 | 0.774 | 0.000549 | 6 | 0.1430 | -4.054 | 1 | 0.0338 | 116.529 | 4 | 0.0824 |
| 33 | I Hate Everything About You | 75 | 231480 | 0.004610 | 0.498 | 0.830 | 0.000000 | 6 | 0.1390 | -5.157 | 0 | 0.0421 | 89.342 | 4 | 0.4530 |
| 34 | Rollin' (Air Raid Vehicle) | 74 | 213760 | 0.005060 | 0.599 | 0.932 | 0.000000 | 9 | 0.2690 | -3.328 | 1 | 0.1800 | 96.234 | 4 | 0.6920 |
| 35 | Fat Lip | 74 | 178266 | 0.000618 | 0.574 | 0.911 | 0.000000 | 9 | 0.0769 | -5.176 | 1 | 0.0715 | 98.075 | 4 | 0.5730 |
| 36 | The Pretender | 11 | 269373 | 0.000917 | 0.433 | 0.959 | 0.000000 | 9 | 0.0280 | -4.040 | 1 | 0.0431 | 172.984 | 4 | 0.3650 |
| 37 | Savior | 73 | 242280 | 0.001640 | 0.549 | 0.930 | 0.000067 | 5 | 0.3900 | -3.468 | 0 | 0.0487 | 112.447 | 4 | 0.5260 |
| 38 | Bodies | 74 | 201960 | 0.003470 | 0.656 | 0.932 | 0.001470 | 4 | 0.1440 | -3.405 | 0 | 0.0708 | 130.936 | 4 | 0.5440 |
| 39 | Sugar, We're Goin Down | 79 | 229093 | 0.008980 | 0.499 | 0.824 | 0.000000 | 7 | 0.1630 | -4.741 | 1 | 0.0794 | 161.977 | 4 | 0.6990 |
| 40 | Last Nite | 70 | 193373 | 0.022300 | 0.624 | 0.899 | 0.000155 | 0 | 0.0975 | -5.710 | 1 | 0.0295 | 104.055 | 4 | 0.7970 |
| 41 | Through Glass | 60 | 282946 | 0.041700 | 0.545 | 0.753 | 0.000000 | 3 | 0.3890 | -4.618 | 0 | 0.0360 | 105.754 | 4 | 0.4340 |
| 42 | The Diary of Jane - Single Version | 69 | 200546 | 0.000055 | 0.374 | 0.961 | 0.083400 | 10 | 0.1380 | -4.421 | 0 | 0.0851 | 167.032 | 4 | 0.3570 |
| 43 | Down With the Sickness | 73 | 279213 | 0.000996 | 0.695 | 0.876 | 0.000007 | 3 | 0.1060 | -4.262 | 0 | 0.0553 | 89.954 | 4 | 0.9410 |
| 44 | Wish You Were Here | 62 | 212733 | 0.000631 | 0.246 | 0.768 | 0.301000 | 9 | 0.1020 | -7.480 | 1 | 0.0387 | 169.873 | 4 | 0.3500 |
| 45 | Youth Of The Nation | 72 | 256240 | 0.008340 | 0.563 | 0.860 | 0.010600 | 8 | 0.3900 | -7.533 | 1 | 0.0621 | 97.867 | 4 | 0.5170 |
| 46 | Vertigo | 57 | 194520 | 0.000153 | 0.391 | 0.831 | 0.001920 | 2 | 0.1410 | -3.892 | 1 | 0.0613 | 139.946 | 4 | 0.6830 |
| 47 | Miss Murder | 71 | 206586 | 0.000213 | 0.309 | 0.875 | 0.001680 | 1 | 0.1130 | -4.490 | 1 | 0.0488 | 143.526 | 4 | 0.7250 |
| 48 | I Bet You Look Good On The Dancefloor | 63 | 173680 | 0.002250 | 0.535 | 0.948 | 0.000000 | 6 | 0.3760 | -4.190 | 0 | 0.0356 | 103.183 | 4 | 0.7780 |
| 49 | Steady, As She Goes | 64 | 215266 | 0.013200 | 0.524 | 0.578 | 0.009210 | 9 | 0.1080 | -4.563 | 1 | 0.1200 | 123.669 | 4 | 0.5370 |
In [180]:
print(data.info())
print(data.describe())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 18835 entries, 0 to 18834
Data columns (total 15 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 song_name 18835 non-null object
1 song_popularity 18835 non-null int64
2 song_duration_ms 18835 non-null int64
3 acousticness 18835 non-null float64
4 danceability 18835 non-null float64
5 energy 18835 non-null float64
6 instrumentalness 18835 non-null float64
7 key 18835 non-null int64
8 liveness 18835 non-null float64
9 loudness 18835 non-null float64
10 audio_mode 18835 non-null int64
11 speechiness 18835 non-null float64
12 tempo 18835 non-null float64
13 time_signature 18835 non-null int64
14 audio_valence 18835 non-null float64
dtypes: float64(9), int64(5), object(1)
memory usage: 2.2+ MB
None
song_popularity song_duration_ms acousticness danceability \
count 18835.000000 1.883500e+04 18835.000000 18835.000000
mean 52.991877 2.182116e+05 0.258539 0.633348
std 21.905654 5.988754e+04 0.288719 0.156723
min 0.000000 1.200000e+04 0.000001 0.000000
25% 40.000000 1.843395e+05 0.024100 0.533000
50% 56.000000 2.113060e+05 0.132000 0.645000
75% 69.000000 2.428440e+05 0.424000 0.748000
max 100.000000 1.799346e+06 0.996000 0.987000
energy instrumentalness key liveness \
count 18835.000000 18835.000000 18835.000000 18835.000000
mean 0.644995 0.078008 5.289196 0.179650
std 0.214101 0.221591 3.614595 0.143984
min 0.001070 0.000000 0.000000 0.010900
25% 0.510000 0.000000 2.000000 0.092900
50% 0.674000 0.000011 5.000000 0.122000
75% 0.815000 0.002570 8.000000 0.221000
max 0.999000 0.997000 11.000000 0.986000
loudness audio_mode speechiness tempo time_signature \
count 18835.000000 18835.000000 18835.000000 18835.000000 18835.000000
mean -7.447435 0.628139 0.102099 121.073154 3.959119
std 3.827831 0.483314 0.104378 28.714456 0.298533
min -38.768000 0.000000 0.000000 0.000000 0.000000
25% -9.044000 0.000000 0.037800 98.368000 4.000000
50% -6.555000 1.000000 0.055500 120.013000 4.000000
75% -4.908000 1.000000 0.119000 139.931000 4.000000
max 1.585000 1.000000 0.941000 242.318000 5.000000
audio_valence
count 18835.000000
mean 0.527967
std 0.244632
min 0.000000
25% 0.335000
50% 0.527000
75% 0.725000
max 0.984000
In [181]:
# Teoretyczny opis modelu regresji liniowej
print("Regresja liniowa to jedna z podstawowych technik statystycznych i uczenia maszynowego, służąca do modelowania zależności między zmienną zależną (Y) a jedną lub wieloma zmiennymi niezależnymi (X). Zakłada istnienie liniowej zależności pomiędzy tymi zmiennymi.\n")
print("Równanie regresji liniowej: y = ax +b\ny – zmienna zależna (objaśniana),\nx – zmienna niezależna (objaśniająca),\na – współczynnik kierunkowy (nachylenie prostej),\nb – wyraz wolny (punkt przecięcia z osią Y)\n")
print("Celem regresji liniowej jest znalezienie takich parametrów a i b, które minimalizują błąd przewidywań, czyli odległości między wartościami rzeczywistymi y a przewidywanymi. Służy do tego metoda najmniejszych kwadratów.")
Regresja liniowa to jedna z podstawowych technik statystycznych i uczenia maszynowego, służąca do modelowania zależności między zmienną zależną (Y) a jedną lub wieloma zmiennymi niezależnymi (X). Zakłada istnienie liniowej zależności pomiędzy tymi zmiennymi. Równanie regresji liniowej: y = ax +b y – zmienna zależna (objaśniana), x – zmienna niezależna (objaśniająca), a – współczynnik kierunkowy (nachylenie prostej), b – wyraz wolny (punkt przecięcia z osią Y) Celem regresji liniowej jest znalezienie takich parametrów a i b, które minimalizują błąd przewidywań, czyli odległości między wartościami rzeczywistymi y a przewidywanymi. Służy do tego metoda najmniejszych kwadratów.
In [182]:
import matplotlib.pyplot as plt
plt.scatter(data['energy'], data['loudness'])
plt.xlabel("Energy")
plt.ylabel("Loudness")
plt.title("Zależność między Energią a Głośnością")
plt.show()
In [183]:
import seaborn as sns
subset = data.iloc[:, :12]
sns.pairplot(subset)
plt.show()
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
In [184]:
import statsmodels.api as sm
data_clean = data[['energy', 'loudness']].replace([np.inf, -np.inf], np.nan).dropna()
X = data_clean['energy']
Y = data_clean['loudness']
# wyraz wolny
X = sm.add_constant(X)
# Budowa modelu
model = sm.OLS(Y, X).fit()
predictions = model.predict(X)
print(model.summary())
OLS Regression Results
==============================================================================
Dep. Variable: loudness R-squared: 0.571
Model: OLS Adj. R-squared: 0.571
Method: Least Squares F-statistic: 2.505e+04
Date: Fri, 16 May 2025 Prob (F-statistic): 0.00
Time: 14:18:16 Log-Likelihood: -44042.
No. Observations: 18835 AIC: 8.809e+04
Df Residuals: 18833 BIC: 8.810e+04
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -16.1598 0.058 -278.599 0.000 -16.273 -16.046
energy 13.5076 0.085 158.261 0.000 13.340 13.675
==============================================================================
Omnibus: 5442.816 Durbin-Watson: 1.261
Prob(Omnibus): 0.000 Jarque-Bera (JB): 25971.752
Skew: -1.328 Prob(JB): 0.00
Kurtosis: 8.103 Cond. No. 6.68
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [185]:
print("Interpretacja wyników:\n")
print("Dopasowanie modelu:")
print("R-squared: 0.571 - (Współczynnik determinacji) - Model tłumaczy 57,1% zmienności zmiennej loudness za pomocą zmiennej energy. To oznacza umiarkowane dopasowanie modelu.")
print("Adj. R-squared: 0.571 - (Skorygowany R²) - Używany przy wielu zmiennych — tutaj równy R², bo model ma tylko jedną zmienną niezależną.")
print("F-statistic: 25050 - (Statystyka F) - Pokazuje, że model jako całość jest istotny statystycznie.")
print("Prob (F-statistic): 0.000 - (p-wartość testu F) - Bardzo niska (0.000) — wskazuje, że model jest statystycznie istotny.")
print("Parametry modelu:")
print("const: -16.1598 - To wyraz wolny (intercept) – głośność (loudness) przy energy = 0. Ma wartość ujemną, co oznacza bardzo cichą lub brak dźwięku")
print("energy: 13.5076 - To nachylenie (slope) – pokazuje, że gdy energia wzrasta o 1 jednostkę, loudness rośnie średnio o 13.51 jednostki. Silna dodatnia zależność.")
print("Omnibus: 5442.816 oraz Prob(Omnibus): 0.000 - Test normalności reszt - Reszty nie są rozkładem normalnym (oznacza możliwe naruszenie jednego z założeń regresji).")
print("Skew: -1.328 - Skośność rozkładu reszt - Ujemna skośność – ogon po lewej stronie, może sugerować nieliniowość lub wartości odstające.")
print("Kurtosis: 8.103 - Kurtoza (spłaszczenie) - Rozkład bardziej spiczasty niż normalny (kurtosis > 3), również oznaka nienormalności.")
print("Durbin-Watson: 1.261 - Test autokorelacji reszt - Idealnie powinno być zbliżone do 2. Tutaj < 2, więc może występować autokorelacja dodatnia – reszty są zależne.")
Interpretacja wyników: Dopasowanie modelu: R-squared: 0.571 - (Współczynnik determinacji) - Model tłumaczy 57,1% zmienności zmiennej loudness za pomocą zmiennej energy. To oznacza umiarkowane dopasowanie modelu. Adj. R-squared: 0.571 - (Skorygowany R²) - Używany przy wielu zmiennych — tutaj równy R², bo model ma tylko jedną zmienną niezależną. F-statistic: 25050 - (Statystyka F) - Pokazuje, że model jako całość jest istotny statystycznie. Prob (F-statistic): 0.000 - (p-wartość testu F) - Bardzo niska (0.000) — wskazuje, że model jest statystycznie istotny. Parametry modelu: const: -16.1598 - To wyraz wolny (intercept) – głośność (loudness) przy energy = 0. Ma wartość ujemną, co oznacza bardzo cichą lub brak dźwięku energy: 13.5076 - To nachylenie (slope) – pokazuje, że gdy energia wzrasta o 1 jednostkę, loudness rośnie średnio o 13.51 jednostki. Silna dodatnia zależność. Omnibus: 5442.816 oraz Prob(Omnibus): 0.000 - Test normalności reszt - Reszty nie są rozkładem normalnym (oznacza możliwe naruszenie jednego z założeń regresji). Skew: -1.328 - Skośność rozkładu reszt - Ujemna skośność – ogon po lewej stronie, może sugerować nieliniowość lub wartości odstające. Kurtosis: 8.103 - Kurtoza (spłaszczenie) - Rozkład bardziej spiczasty niż normalny (kurtosis > 3), również oznaka nienormalności. Durbin-Watson: 1.261 - Test autokorelacji reszt - Idealnie powinno być zbliżone do 2. Tutaj < 2, więc może występować autokorelacja dodatnia – reszty są zależne.
In [186]:
# Sprawdzenie założeń modelu
from scipy.stats import normaltest
# Normalność reszt
residuals = model.resid
sns.histplot(residuals, kde=True)
plt.title("Histogram reszt")
plt.show()
stat, p = normaltest(residuals)
print(f"Normal test: stat={stat:.3f}, p={p:.3f}")
print(f" p = {p:.3f} co jest mniejsze niż 0.05 więc reszty zdecydowanie nie są z rozkładu normalnego")
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
Normal test: stat=5442.816, p=0.000 p = 0.000 co jest mniejsze niż 0.05 więc reszty zdecydowanie nie są z rozkładu normalnego
In [187]:
# Homoskedastyczność (stałość wariancji reszt)
fitted_vals = model.fittedvalues
plt.scatter(fitted_vals, residuals)
plt.axhline(0, color='red')
plt.title("Reszty vs przewidywane wartości")
plt.xlabel("Przewidywane")
plt.ylabel("Reszty")
plt.show()
print("Jak widać na wykresie punkty są rozsiane losowo wokół linii 0.\nBrak wyraźnego wzoru (brak rosnącej/malejącej wariancji z wyjątkiem początkowych wartości).\nRozrzut punktów jest w miarę stały w całym zakresie wartości przewidywanych.")
Jak widać na wykresie punkty są rozsiane losowo wokół linii 0. Brak wyraźnego wzoru (brak rosnącej/malejącej wariancji z wyjątkiem początkowych wartości). Rozrzut punktów jest w miarę stały w całym zakresie wartości przewidywanych.
In [188]:
from statsmodels.stats.stattools import durbin_watson
# Autokorelacja reszt (Durbin-Watson)
dw = durbin_watson(residuals)
print(f"Durbin-Watson = {dw:.2f}")
print("Wynik < 2 sugeruje dodatnią autokorelację. Może to oznaczać, że w danych jest jakiś trend, którego model nie uwzględnił")
Durbin-Watson = 1.26 Wynik < 2 sugeruje dodatnią autokorelację. Może to oznaczać, że w danych jest jakiś trend, którego model nie uwzględnił
In [189]:
# Współliniowość (korelacje)
sns.heatmap(data_clean[['energy', 'loudness']].corr(), annot=True, cmap='coolwarm')
plt.title("Korelacja: energy vs loudness")
plt.show()
print("Macierz wskazuje na silną dodatną korelację > 0.7")
Macierz wskazuje na silną dodatną korelację > 0.7
In [190]:
# Wartości odstające i wpływowe
sns.boxplot(x=residuals)
plt.title("Boxplot reszt")
plt.show()
print("Pudełko (box) – obejmuje 50% danych, od pierwszego (Q1) do trzeciego kwartylu (Q3).")
print("Linia w środku pudełka – to mediana (czyli środkowa wartość)")
print("„Wąsy” (whiskers) – sięgają do wartości w zakresie Q1 − 1.5×IQR i Q3 + 1.5×IQR.")
print("Punkty poza wąsami – to wartości odstające (outliers), czyli reszty znacznie odbiegające od większości.")
Pudełko (box) – obejmuje 50% danych, od pierwszego (Q1) do trzeciego kwartylu (Q3). Linia w środku pudełka – to mediana (czyli środkowa wartość) „Wąsy” (whiskers) – sięgają do wartości w zakresie Q1 − 1.5×IQR i Q3 + 1.5×IQR. Punkty poza wąsami – to wartości odstające (outliers), czyli reszty znacznie odbiegające od większości.
In [191]:
influence = model.get_influence()
(c, p) = influence.cooks_distance
# Wykres odległości Cooka
plt.stem(np.arange(len(c)), c, markerfmt=",")
plt.title("Odległość Cooka")
plt.xlabel("Obserwacja")
plt.ylabel("Cook's Distance")
plt.show()
# Usunięcie wpływowych punktów (np. > 4/n)
influential_points = np.where(c > 2 / len(X))[0]
data_cleaned = data.drop(data.index[influential_points])
In [192]:
X_clean = data_cleaned['energy']
y_clean = data_cleaned['loudness']
X_clean = sm.add_constant(X_clean)
model_clean = sm.OLS(y_clean, X_clean).fit()
print(model_clean.summary())
OLS Regression Results
==============================================================================
Dep. Variable: loudness R-squared: 0.635
Model: OLS Adj. R-squared: 0.635
Method: Least Squares F-statistic: 2.903e+04
Date: Fri, 16 May 2025 Prob (F-statistic): 0.00
Time: 14:18:20 Log-Likelihood: -32436.
No. Observations: 16710 AIC: 6.488e+04
Df Residuals: 16708 BIC: 6.489e+04
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const -14.7672 0.049 -304.172 0.000 -14.862 -14.672
energy 11.8661 0.070 170.388 0.000 11.730 12.003
==============================================================================
Omnibus: 278.940 Durbin-Watson: 1.565
Prob(Omnibus): 0.000 Jarque-Bera (JB): 278.357
Skew: -0.294 Prob(JB): 3.59e-61
Kurtosis: 2.770 Cond. No. 7.81
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [193]:
clean_residuals = model_clean.resid
sns.boxplot(x=clean_residuals)
plt.title("Boxplot reszt po usunięciu wartości wpływowych")
plt.show()
In [194]:
influence = model_clean.get_influence()
(c, p) = influence.cooks_distance
# Wykres odległości Cooka
plt.stem(np.arange(len(c)), c, markerfmt=",")
plt.title("Odległość Cooka po sunięciu wartości wpływowych")
plt.xlabel("Obserwacja")
plt.ylabel("Cook's Distance")
plt.show()
In [195]:
plt.scatter(data_cleaned['energy'], data_cleaned['loudness'])
plt.xlabel("Energy")
plt.ylabel("Loudness")
plt.title("Zależność między Energią a Głośnością po usun. wart. wpływowych")
Out[195]:
Text(0.5, 1.0, 'Zależność między Energią a Głośnością po usun. wart. wpływowych')
In [196]:
# Normalność reszt
sns.histplot(clean_residuals, kde=True)
plt.title("Histogram reszt po usunięciu wartości wpływowych")
plt.show()
stat, p = normaltest(clean_residuals)
print(f"Normal test: stat={stat:.3f}, p={p:.3f}")
C:\Users\A\.conda\envs\tum_py313\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
with pd.option_context('mode.use_inf_as_na', True):
Normal test: stat=278.940, p=0.000
In [197]:
# Homoskedastyczność (stałość wariancji reszt)
fitted_vals = model_clean.fittedvalues
plt.scatter(fitted_vals, clean_residuals)
plt.axhline(0, color='red')
plt.title("Reszty vs przewidywane wartości po usunięciu wartości wpływowych")
plt.xlabel("Przewidywane")
plt.ylabel("Reszty")
plt.show()
In [198]:
dw = durbin_watson(clean_residuals)
print(f"Durbin-Watson = {dw:.2f}")
print("Wynik < 2 sugeruje dodatnią autokorelację.")
Durbin-Watson = 1.57 Wynik < 2 sugeruje dodatnią autokorelację.
In [199]:
# Współliniowość (korelacje)
sns.heatmap(data_cleaned[['energy', 'loudness']].corr(), annot=True, cmap='coolwarm')
plt.title("Korelacja: energy vs loudness po usunięciu wart. wpływowych")
plt.show()
print("widać poprawę korelacji = 0.8")
widać poprawę korelacji = 0.8
In [200]:
from sklearn.model_selection import KFold
import statsmodels.api as sm
import numpy as np
kf = KFold(n_splits=5, shuffle=True, random_state=42)
X_clean = data_cleaned['energy']
y_clean = data_cleaned['loudness']
r2_train_list = []
r2_test_list = []
rse_train_list = []
rse_test_list = []
for train_index, test_index in kf.split(X_clean):
X_train, X_test = X_clean.iloc[train_index], X_clean.iloc[test_index]
y_train, y_test = y_clean.iloc[train_index], y_clean.iloc[test_index]
X_train_const = sm.add_constant(X_train)
X_test_const = sm.add_constant(X_test)
model = sm.OLS(y_train, X_train_const).fit()
y_train_pred = model.predict(X_train_const)
y_test_pred = model.predict(X_test_const)
r2_train = model.rsquared
r2_test = 1 - np.sum((y_test - y_test_pred)**2) / np.sum((y_test - np.mean(y_test))**2)
rse_train = np.sqrt(np.mean((y_train - y_train_pred)**2))
rse_test = np.sqrt(np.mean((y_test - y_test_pred)**2))
r2_train_list.append(r2_train)
r2_test_list.append(r2_test)
rse_train_list.append(rse_train)
rse_test_list.append(rse_test)
print("Średnie R^2 (trening):", np.mean(r2_train_list))
print("Średnie R^2 (test):", np.mean(r2_test_list))
print("Średnie RSE (trening):", np.mean(rse_train_list))
print("Średnie RSE (test):", np.mean(rse_test_list))
print("Podsumowanie:\nModel działa stabilnie - błędy są podobne na zbiorze treningowym i testowym.\nModel nie jest przeuczony co sugeruje RSE.\nR^2 (R-squared) = 0.63 oznacza, że model wyjaśnia ~63% zmienności loudness na podstawie energy. Ma średnie dopasowanie ale może to wynikać z faktu że dane które analizujemy nie były idealnie liniowe.")
Średnie R^2 (trening): 0.6347230452604158 Średnie R^2 (test): 0.6345889431517886 Średnie RSE (trening): 1.6856341906057757 Średnie RSE (test): 1.6857654494805416 Podsumowanie: Model działa stabilnie - błędy są podobne na zbiorze treningowym i testowym. Model nie jest przeuczony co sugeruje RSE. R^2 (R-squared) = 0.63 oznacza, że model wyjaśnia ~63% zmienności loudness na podstawie energy. Ma średnie dopasowanie ale może to wynikać z faktu że dane które analizujemy nie były idealnie liniowe.